package com.lingjtx.auth.service.impl;

import com.alibaba.fastjson2.JSON;
import com.lingjtx.auth.config.UserServiceContextWrapper;
import com.lingjtx.auth.dao.UserMapper;
import com.lingjtx.auth.po.LoginConfirmPo;
import com.lingjtx.auth.po.UpdatePswPo;
import com.lingjtx.auth.service.UserHandleService;
import com.lingjtx.auth.service.UserService;
import com.lingjtx.common.core.algorithm.Algorithm;
import com.lingjtx.common.core.algorithm.MD5Util;
import com.lingjtx.common.core.constant.CodeMessage;
import com.lingjtx.common.core.constant.Const;
import com.lingjtx.common.core.constant.RedisConfig;
import com.lingjtx.common.core.constant.RegexExp;
import com.lingjtx.common.core.token.AuthToken;
import com.lingjtx.common.core.token.UserVo;
import com.lingjtx.common.core.util.LinkGenerator;
import com.lingjtx.common.core.util.StringUtil;
import com.lingjtx.common.other.helper.MessageManager;
import com.lingjtx.common.other.helper.WeiXinOauth;
import com.lingjtx.common.redis.service.IRedisService;
import com.lingjtx.common.security.interceptor.UserContext;
import com.lingjtx.common.security.model.LoginPo;
import com.lingjtx.common.thread.pooling.ObjectPoolTemplate;
import jakarta.mail.Session;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;

import java.net.http.HttpClient;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

@Service
public class UserServiceImpl implements UserService {

    private final IRedisService redisService;
    private final TemplateEngine templateEngine;
    private final UserMapper userMapper;
    private final UserServiceContextWrapper wrapper;

    private final ObjectPoolTemplate<HttpClient> httpObjectPoolTemplate;
    private final ObjectPoolTemplate<Session> mailObjectPoolTemplate;

    public UserServiceImpl(IRedisService redisService, TemplateEngine templateEngine, UserMapper userMapper, UserServiceContextWrapper wrapper,
                           @Qualifier("httpObjectPoolTemplate") ObjectPoolTemplate<HttpClient> httpObjectPoolTemplate,
                           @Qualifier("mailObjectPoolTemplate") ObjectPoolTemplate<Session> mailObjectPoolTemplate) {
        this.redisService = redisService;
        this.templateEngine = templateEngine;
        this.userMapper = userMapper;
        this.wrapper = wrapper;
        this.httpObjectPoolTemplate = httpObjectPoolTemplate;
        this.mailObjectPoolTemplate = mailObjectPoolTemplate;
    }

    // 是否开启被踢下线：true 开启
    private static final boolean offline = true;


    @Override
    public String sendSmsCode(String identifier, Integer smsType) {
        if (StringUtil.isRegexVerify(identifier, RegexExp.EMAIL)) { // 是邮箱
            MessageManager.INSTANCE.sendCode(identifier, MessageManager.MessagePlatform.MAIL, CodeMessage.codeOf(smsType), redisService, mailObjectPoolTemplate, templateEngine);
            return "邮箱验证码已发送，请在10分钟内验证";
        }
        if (StringUtil.isRegexVerify(identifier, RegexExp.MOBILE)) {
            MessageManager.INSTANCE.sendCode(identifier, MessageManager.MessagePlatform.SMS, CodeMessage.codeOf(smsType), redisService, httpObjectPoolTemplate, null);
            return "短信验证码已发送，请在10分钟内验证";
        }
        MessageManager.INSTANCE.sendCode(identifier, MessageManager.MessagePlatform.WEXIN, CodeMessage.codeOf(smsType), redisService, httpObjectPoolTemplate, null);
        return "微信验证码已发送，请在10分钟内验证";
    }

    @Override
    public UserVo loginByPassword(LoginPo model) {
        UserVo user = userMapper.selectUserByIdentifier(model.getIdentifier(), model.getUserType());
        if (Objects.nonNull(user)) {
            if (MD5Util.MD5EncodeUtf8(model.getPassword()).equals(user.getUserPsw())) {
                setParams(user, model);
                return AuthToken.createToken(user, model.getOperationDevice(), model.getDeviceUid(), redisService, offline);
            }
        }
        return null;
    }

    @Override
    public UserVo loginByOther(LoginPo model) {
        UserVo user = userMapper.selectUserByIdentifier(model.getIdentifier(), model.getUserType());
        if (Objects.nonNull(user)) {
            setParams(user, model);
            return AuthToken.createToken(user, model.getOperationDevice(), model.getDeviceUid(), redisService, offline);
        }
        return null;
    }

    @Override
    public UserVo refreshToken(String token, LoginPo model) {
        return AuthToken.refreshToken(token, offline, redisService, (userId, userType) -> {
            UserVo user = userMapper.selectUserByUserUid(userId, userType);
            setParams(user, model);
            user.setUserType(userType);
            return user;
        });
    }

    @Override
    public String loginByUrl(LoginPo model) {
        // 扫码状态：0-未扫码；1-已扫码；2-确认登录；3-二维码无效
        String qrcoceUid = UUID.randomUUID().toString();
        redisService.set(RedisConfig.KEY_SCANLOGIN_STATUS + qrcoceUid, Const.ScanStatus.NOT_SCAN, RedisConfig.TIMEOUT_5_MIN);
        model.setQrcodeUid(qrcoceUid);
        String uri = "/user/scan_login";
        if (StringUtil.isNotEmpty(model.getIdentifier())) { // 生成的链接参数中有token传入，绑定微信号业务
            uri = "/user/scan_bound_wx";
        }
        return generatorLoginUrl(model, uri);
    }

    @Override
    public Map<String, Integer> scanStatus(LoginConfirmPo model) {
        // 每秒请求一次，查询扫码状态：0-未扫码；1-已扫码；2-确认登录；3-二维码无效
        LoginPo entity = (LoginPo) LinkGenerator.getData(model.getKey(), redisService);
        Map<String, Integer> result = new HashMap<>(1);
        if (Objects.nonNull(entity)) {
            Integer status = (Integer) redisService.get(RedisConfig.KEY_SCANLOGIN_STATUS + entity.getQrcodeUid());
            result.put("status", status);
            return result;
        }
        result.put("status", Const.ScanStatus.EXPIRE);
        return result;
    }

    @Override
    public boolean confirmLogin(LoginConfirmPo model) {
        LoginPo entity = (LoginPo) LinkGenerator.getData(model.getKey(), redisService);
        if (Objects.nonNull(entity)) {
            UserVo user = UserContext.getUser();
            // 更改扫码状态为2：扫码状态：0-未扫码；1-已扫码；2-确认登录；3-二维码无效
            redisService.set(RedisConfig.KEY_SCANLOGIN_STATUS + entity.getQrcodeUid(), Const.ScanStatus.CONFIRM);
            // 有openid，说明是走绑定微信号功能，C端无需绑定微信号操作
            if (StringUtil.isNotEmpty(model.getOpenid()) && !Const.UserType.TOC.equals(user.getUserType())) {
                // 具体的绑定微信号流程
                UserHandleService<Object> userHandleService = wrapper.getUserService(user.getUserType());
                userHandleService.boundWxOpenid(user.getUserId(), model.getOpenid());
            } else {
                UserVo newUser = AuthToken.createToken(user, entity.getOperationDevice(), entity.getDeviceUid(), redisService, offline);
                redisService.set(RedisConfig.KEY_SCANLOGIN_TOKEN + entity.getQrcodeUid(), JSON.toJSONString(newUser), RedisConfig.TIMEOUT_5_MIN);
            }
            return true;
        }
        return false;
    }

    @Override
    public UserVo bizlogin(LoginConfirmPo model) {
        // 确认登录之后(扫码状态为2的时候)，返回的登录信息
        LoginPo entity = (LoginPo) LinkGenerator.getData(model.getKey(), redisService);
        if (Objects.nonNull(entity)) {
            UserVo user = (UserVo) redisService.get(RedisConfig.KEY_SCANLOGIN_TOKEN + entity.getQrcodeUid());
            if (user != null) {
                redisService.delete(RedisConfig.KEY_SCANLOGIN_TOKEN + entity.getQrcodeUid(), RedisConfig.KEY_SCANLOGIN_STATUS + entity.getQrcodeUid());
                return user;
            }
        }
        return null;
    }

    @Override
    public boolean boundEmail(UpdatePswPo model) {
        UserVo user = UserContext.getUser();
        if (StringUtil.isNotEmpty(model.getEmail())
                && StringUtil.isRegexVerify(model.getEmail(), RegexExp.EMAIL)) {
            user.setEmail(model.getEmail());
            MessageManager.INSTANCE.activeEmail(
                    model.getEmail(),
                    user,
                    redisService,
                    mailObjectPoolTemplate,
                    templateEngine
            );
            return true;
        }
        return false;
    }

    @Override
    public Object getUserInfo() {
        UserVo user = UserContext.getUser();
        UserHandleService<Object> userHandleService = wrapper.getUserService(user.getUserType());
        return userHandleService.getUserInfo();
    }

    @Override
    public boolean updatePassword(UpdatePswPo model) {
        UserVo user = UserContext.getUser();
        UserHandleService<Object> userHandleService = wrapper.getUserService(user.getUserType());
        return userHandleService.updatePassword(model);
    }

    @Override
    public boolean updateMobile(UpdatePswPo model) {
        UserVo user = UserContext.getUser();
        UserHandleService<Object> userHandleService = wrapper.getUserService(user.getUserType());
        return userHandleService.updateMobile(model);
    }

    @Override
    public boolean updateUserInfo(Object model) {
        UserVo user = UserContext.getUser();
        UserHandleService<Object> userHandleService = wrapper.getUserService(user.getUserType());
        return userHandleService.updateUserInfo(model);
    }

    @Override
    public boolean deleteAccount(LoginPo model) {
        UserVo user = UserContext.getUser();
        UserHandleService<Object> userHandleService = wrapper.getUserService(user.getUserType());
        return userHandleService.deleteAccount(model);
    }

    @Override
    public void process(String key) {
        UserVo user = (UserVo) LinkGenerator.getData(key, redisService);
        if (Objects.nonNull(user) && StringUtil.isNotEmpty(user.getUserType())) {
            UserHandleService<Object> userHandleService = wrapper.getUserService(user.getUserType());
            userHandleService.process(key);
        }
    }

    @Override
    public String getBaseWeixinOauthUrl() {
        return WeiXinOauth.INSTANCE.getBaseWeixinOauthUrl(redisService);
    }

    private UserVo setParams(UserVo user, LoginPo model) {
        user.setUserType(model.getUserType());
        if (!Const.UserType.TOC.equals(model.getUserType())) {
            user.setCompanyId(model.getCompanyId());
            user.setAdminAccount(model.getAdminAccount());
            user.setCertificationStatus(model.getCertificationStatus());
            user.setScopeType(model.getScopeType());
            user.setScopeList(model.getScopeList());
            user.setPerms(model.getPerms());
        }
        return user;
    }

    private String generatorLoginUrl(LoginPo model, String uri) {
        LinkGenerator linkGenerator = LinkGenerator.init()
                .setLinkGeneratorHandler(new LinkGenerator.NonRestfulPolicy())
                .setAlgorithm(Algorithm.RANDOM)
                .setBaseurl(Const.SERVER_URL)
                .setUri(uri)
                .setChecks(1L)
                .setExpire(RedisConfig.TIMEOUT_5_MIN)
                .setLinkType(LinkGenerator.LinkType.IDENTITY)
                .setData(model)
                .setRedisOptions(redisService)
                .build();
        return linkGenerator.generator();
    }
}
